package com.zk.api.parser;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.zk.api.git.GitService;
import com.zk.api.model.ClassModel;
import com.zk.api.model.MethodModel;
import com.zk.api.model.ParamModel;

@Service("parserService")
@PropertySource("classpath:regexConfigOfSpring.properties")
public class Parser {

	@Value("${regex.package.nameInfoParser}")
	String regexPackageNameInfoParser;

	@Value("${regex.class.importInfoParser}")
	String regexClassImportInfoParser;

	@Value("${regex.class.urlParser}")
	String regexClassUrlParser;

	@Value("${regex.class.authorInfoParser}")
	String regexClassAuthorInfoParser;

	@Value("${regex.class.versionInfoParser}")
	String regexClassVersionInfoParser;

	@Value("${regex.class.nameInfoParser}")
	String regexClassNameInfoParser;

	@Value("${regex.method.remarkInfoParser}")
	String regexMethodRemarkInfoParser;

	@Value("${regex.class.remarkInfoParser}")
	String regexClassRemarkInfoParser;
	
	@Value("${regex.method.nameInfoParser}")
	String regexMethodNameInfoParser;

	@Value("${regex.method.typeInfoParser}")
	String regexMethodTypeInfoParser;

	@Value("${regex.method.returnInfoParser}")
	String regexMethodReturnInfoParser;

	@Value("${regex.param.remarkInfoParser}")
	String regexParamRemarkInfoParser;

	@Value("${regex.param.needInfoParser}")
	String regexParamNeedInfoParser;

	@Value("${regex.param.valueInfoParser}")
	String regexParamValueInfoParser;

	@Value("${regex.method.sparator}")
	String regexMethodSparator;

	@Value("${regex.file.removeParser}")
	String regexFileRemoveParser;

	@Value("${regex.file.controllerParser}")
	String regexFileControllerParser;

	@Value("${regex.param.devideInfoParser}")
	String regexParamDevideInfoParse;

	@Autowired
	private GitService gitService;

	ConcurrentHashMap<String, String> local = new ConcurrentHashMap<String, String>();

	List<String> requestType = Arrays.asList("GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "TRACE", "Get",
			"Head", "Post", "Put", "Patch", "Delete", "Options", "Trace");

	/**
	 * 开始构建
	 * 
	 * @param controllerStrings 控制器源代码
	 * @return
	 */
	public List<ClassModel> startBuild(List<String> controllerStrings) {
		List<ClassModel> models = new ArrayList<ClassModel>();
		for (String controller : controllerStrings) {
			String[] removes = regexFileRemoveParser.split("&");
			for (String remove : removes) {
				controller = controller.replaceAll(remove, " ");
			}
			if (parserByRegexs(regexFileControllerParser, controller) != null) {
				ClassModel clazz = new ClassModel();
				clazz.setText(controller);
				buildClassInfo(clazz);
				System.out.println("解析：" + clazz.getName());
				models.add(clazz);
			} else {
				// 存为备用类
				String packageName = parserByRegexs(regexPackageNameInfoParser, controller);
				String clazzName = parserByRegexs(regexClassNameInfoParser, controller);
				if (!StringUtils.isEmpty(packageName) && !StringUtils.isEmpty(clazzName))
					local.put(packageName + clazzName, controller);
			}
		}
		return models;
	}

	/**
	 * 开始构建
	 * 
	 * @param f 项目路径
	 * @return
	 */
	public List<ClassModel> startBuild(File f) {
		List<String> controllers = new ArrayList<String>();
		try {
			long d1=System.currentTimeMillis();
			getFilesByDir(f, controllers, ".java", (String s) -> {
				int a = Math.max(s.indexOf("@RestController"), s.indexOf("@Controller"));
				int c = s.indexOf(" class ");
				if ((a > -1) && s.indexOf("Mapping") != -1 && a < c)
					return true;
				return false;
			});
			System.out.println("shaomiaowenjian:"+(System.currentTimeMillis()-d1));
		} catch (IOException e) {
			e.printStackTrace();
		}
		return startBuild(controllers);
	}

	/**
	 * 开始构建
	 * 
	 * @param ... git配置
	 * @return
	 */
	public List<ClassModel> startBuild(String baseUrl, String dir, String user, String pass, String branch,
			String revision) {
		gitService.getGitPull(baseUrl, dir, user, pass, branch, revision);
		return startBuild(new File(dir));
	}

	/**
	 * 构建类信息，根url，版本，作者等
	 * 
	 * @param model
	 * @param clazzStr 控制器源码
	 */
	void buildClassInfo(ClassModel model) {
		// TODO 设置类名称，根url。。。
		if (!StringUtils.isEmpty(model.getText())) {
			String str = convertToDeal(model.getText());
			model.setPackageName(parserByRegexs(regexPackageNameInfoParser, str));
			String rootUrl = parserByRegexs(regexClassUrlParser, str);
			model.setAuthor(parserByRegexs(regexClassAuthorInfoParser, str));
			model.setVersion(parserByRegexs(regexClassVersionInfoParser, str));
			model.setName(parserByRegexs(regexClassNameInfoParser, str));
			String remark = parserByRegexs(regexClassRemarkInfoParser, str);
			model.setRemark(remark==null?"":remark.replaceAll("\\*", ""));
			String methodStr = model.getText().substring(model.getText().indexOf("{") + 1,
					model.getText().lastIndexOf("}"));
			String[] methods = convertToDeal(methodStr).split(regexMethodSparator);
			model.setMethod(new ArrayList<MethodModel>());
			for (String m : methods) {
				if (!StringUtils.isEmpty(m) && parserByRegexs(regexClassUrlParser, m) != null) {
					MethodModel method = new MethodModel();
					method.setMethodUrl(rootUrl == null ? "" : rootUrl);
					method.setText(m + ")");
					buildMethodInfo(method);
					model.getMethod().add(method);
				}
			}
		}
	}

	/**
	 * 构建方法信息，方法url，名称，备注，类型等
	 * 
	 * @param model
	 * @param methodStr
	 */
	void buildMethodInfo(MethodModel method) {
		// TODO 设置方法url，方法类型。。。
		String str = method.getText();
		//escapeExprSpecialWord(str);
		if (!StringUtils.isEmpty(str)) {
			String url = parserByRegexs(regexClassUrlParser, str);
			method.setMethodUrl((method.getMethodUrl() + "/" + (url == null ? "" : url)).replace("//", "/"));
			method.setMethodRealUrl(method.getMethodUrl());
			String type = parserByRegexs(regexMethodTypeInfoParser, str);
			if (type != null) {
				for (String requestTyp : requestType) {
					if (type.toUpperCase().endsWith(requestTyp)) {
						method.setMethodType(requestTyp);
					}
				}
			}
			String remark = parserByRegexs(regexMethodRemarkInfoParser, str);
			method.setMethodRemark(remark==null?"":remark.replaceAll("\\*", ""));
			String paramM = getParamStr(str);
			method.setMethodParam(new ArrayList<ParamModel>());
			List<String> params = getRealArr(Arrays.asList(paramM.split(",")), m -> {
				return countCharAppear(m, Arrays.asList('(', ')')) % 2 != 0
						|| countCharAppear(m, Arrays.asList('(')) != countCharAppear(m, Arrays.asList(')'))
						|| countCharAppear(m, Arrays.asList('<', '>')) % 2 != 0
						|| countCharAppear(m, Arrays.asList('<')) != countCharAppear(m, Arrays.asList('>'));
			}, n -> {
				return countCharAppear(n, Arrays.asList('(', ')')) % 2 == 0
						&& countCharAppear(n, Arrays.asList('(')) == countCharAppear(n, Arrays.asList(')'))
						&& countCharAppear(n, Arrays.asList('<', '>')) % 2 == 0
						&& countCharAppear(n, Arrays.asList('<')) == countCharAppear(n, Arrays.asList('>'));
			});
			for (String m : params) {
				ParamModel param = new ParamModel();
				param.setText(m);
				buildParamInfo(param);
				param.setParamRemark(parserByRegexs(".*?@param.*?" + (param.getParamName()) + "(.*?)\\*", str));
				method.getMethodParam().add(param);
			}
			String mn = str.replace(paramM, "");
			if (mn.lastIndexOf("(") != -1)
				mn = mn.substring(0, mn.lastIndexOf("("));
			List<String> abc = getRealArr(Arrays.asList(mn.split("[,| ]")), m -> {
				return (countCharAppear(m, Arrays.asList('<', '>')) % 2 != 0
						|| countCharAppear(m, Arrays.asList('<')) != countCharAppear(m, Arrays.asList('>')));
			}, n -> {
				return countCharAppear(n, Arrays.asList('<', '>')) % 2 == 0
						&& countCharAppear(n, Arrays.asList('<')) == countCharAppear(n, Arrays.asList('>'));
			});
			method.setMethodName(abc.get(0));
			method.setMethodReturn(abc.get(1));
			method.setMethodReturnRemark(parserByRegexs(regexMethodReturnInfoParser, str));
		}
	}

	/**
	 * 构建参数信息，参数类型，参数默认值，参数名称等
	 * 
	 * @param model
	 * @param paramStr
	 */
	void buildParamInfo(ParamModel param) {
		// TODO 设置参数名称，类型，默认值。。。
		String paramStr = param.getText();
		// 提取参数名和参数类型
		String[] tempP = new String[] {};
		tempP = paramStr.split(regexParamDevideInfoParse);
		for (int t = tempP.length - 1; t > 0; t--) {
			if (tempP[t] != null
					&& (countCharAppear(tempP[t], Arrays.asList('<', '>')) % 2 != 0 || countCharAppear(tempP[t],
							Arrays.asList('<')) != countCharAppear(tempP[t], Arrays.asList('>')))) {
				int m = t;
				while (m-- > 0) {
					tempP[t] = tempP[m] + "," + tempP[t];
					if (countCharAppear(tempP[t], Arrays.asList('<', '>')) % 2 == 0 && countCharAppear(tempP[t],
							Arrays.asList('<')) == countCharAppear(tempP[t], Arrays.asList('>'))) {
						t = m;
						tempP[m] = null;
						break;
					}
					tempP[m] = null;
				}
			}
		}
		int x = tempP.length;
		while (--x >= 0) {
			if (!StringUtils.isEmpty(tempP[x])) {
				param.setParamName(tempP[x]);
				break;
			}
		}
		while (--x >= 0) {
			if (!StringUtils.isEmpty(tempP[x])) {
				param.setParamType(tempP[x]);
				break;
			}
		}
		// 提取默认值
		param.setParamValue(parserByRegexs(regexParamValueInfoParser, paramStr));
		// 提取是否必须
		param.setParamValue(parserByRegexs(regexParamNeedInfoParser, paramStr));
		if ((param.getParamNeed() != null && (param.getParamNeed().indexOf("false") != -1))
				|| (paramStr.indexOf("defaultValue") != -1) || (paramStr.indexOf("@") == -1)) {
			param.setParamNeed("false");
		} else {
			param.setParamNeed("true");
		}
		// 提取默认值
		param.setParamValue(parserByRegexs(regexParamValueInfoParser, paramStr));
	}

	/**
	 * 匹配字符串中符合正则的字符串或数组
	 * 
	 * @param regexs 正则集合
	 * @param str    待匹配字符串
	 * @return
	 */
	static String parserByRegexs(String regexArr, String str) {
		Pattern pattern = Pattern.compile(regexArr);
		Matcher matcher = pattern.matcher(str);
		if (matcher.find()) {
			int i=countCharAppear(regexArr, Arrays.asList('|'));
			for(int j=1;j<=(i+1);j++) {
				if(matcher.group(j)!=null) {
					return matcher.group(j);
				}
			}
		}
		return null;
	}
	
	public static void main(String[] args) {
		System.out.print(parserByRegexs(".*?@.*?defaultValue.*?=.*?\"(.*?)\"","@RequestParam(required = false, defaultValue = \"1\") int pageNum,"));
	}

	/**
	 * 匹配字符串中符合正则的字符串或数组
	 * 
	 * @param regexs 正则集合
	 * @param str    待匹配字符串
	 * @return
	 */
	String parserByRegexsAnd(String regexArr, String str) {
		String[] regexs = regexArr.split("&");
		for (String regex : regexs) {
			Pattern pattern = Pattern.compile(regex);
			Matcher matcher = pattern.matcher(str);
			if (matcher.find())
				str = matcher.group(1);
			else
				return null;
		}
		return str;
	}

	public static void getFilesByDir(File f, List<String> fileStrs, String fileType, Function<String, Boolean> fun)
			throws IOException {
		System.out.println("扫描文件：" + f.getAbsolutePath());
		if (f.exists()) {
			if (f.isFile() && (f.getAbsolutePath().endsWith(fileType))) {
				StringBuffer buffer = new StringBuffer();
				BufferedReader bf = new BufferedReader(new FileReader(f));
				String s = null;
				while ((s = bf.readLine()) != null) {
					// 使用readLine方法，一次读一行
					buffer.append(s.trim()).append(" ");
				}
				if (fun.apply(buffer.toString()))
					fileStrs.add(buffer.toString());
			} else if (f.isDirectory()) {
				for (File file : f.listFiles()) {
					getFilesByDir(file, fileStrs, fileType, fun);
				}
			}
		}
	}
	
	public static void getFilesByDir1(File f, List<String> fileStrs, String fileType, Function<String, Boolean> fun)
			throws IOException {System.out.println("扫描文件：" + f.getAbsolutePath());
			if (f.exists()) {
				if (f.isFile() && (f.getAbsolutePath().endsWith(fileType))) {
					FileInputStream fileInputStream = new FileInputStream(f);
		            // 把每次读取的内容写入到内存中，然后从内存中获取
		            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		            byte[] buffer = new byte[1024];
		            int len = 0;
		            // 只要没读完，不断的读取
		            while ((len = fileInputStream.read(buffer)) != -1) {
		                outputStream.write(buffer, 0, len);
		            }
		            // 得到内存中写入的所有数据
		            byte[] data = outputStream.toByteArray();
		            fileInputStream.close();
		            String res = new String(data);
					if (fun.apply(res))
						fileStrs.add(res);
				} else if (f.isDirectory()) {
					for (File file : f.listFiles()) {
						getFilesByDir1(file, fileStrs, fileType, fun);
					}
				}
			}}
	
	public static void getFilesByDir2(File f, List<String> fileStrs, String fileType, Function<String, Boolean> fun)
			throws IOException {System.out.println("扫描文件：" + f.getAbsolutePath());
			if (f.exists()) {
				if (f.isFile() && (f.getAbsolutePath().endsWith(fileType))) {
					FileInputStream in = new FileInputStream(f);
			        long size = in.available();
			        RandomAccessFile out = new RandomAccessFile("d:/2.txt", "rw");
			        FileChannel inc = in.getChannel();
			        MappedByteBuffer bf = inc.map(FileChannel.MapMode.READ_ONLY, 0, size);
			        FileChannel outc = out.getChannel();
			        MappedByteBuffer outbf = outc.map(FileChannel.MapMode.READ_WRITE, 0, size);
			        outbf.put(bf);
			        StringBuffer res = new StringBuffer();
			        String a = null;
			        while((a=out.readLine())!=null) {
			        	res.append(a);
			        }
					if (fun.apply(res.toString())) {
						System.out.println("扫描到文件---：" + f.getAbsolutePath());
						fileStrs.add(res.toString());
					}
				} else if (f.isDirectory()) {
					for (File file : f.listFiles()) {
						getFilesByDir2(file, fileStrs, fileType, fun);
					}
				}
			}}


	/**
	 * 方法体去掉
	 * 
	 * @param controller
	 * @return
	 */
	String convertToDeal(String controller) {
		// System.out.println(controller);
		List<Integer> a = new ArrayList<Integer>();
		List<Integer> b = new ArrayList<Integer>();
		List<Integer> c = new ArrayList<Integer>();
		List<Integer> d = new ArrayList<Integer>();
		for (int i = 0; i < controller.length(); i++) {
			if ("{".charAt(0) == (controller.charAt(i))) {
				a.add(i);
			}
			if ("}".charAt(0) == (controller.charAt(i))) {
				b.add(i);
			}
			if (i > 0 && "{".charAt(0) == (controller.charAt(i)) && "/".charAt(0) == (controller.charAt(i - 1))) {
				d.add(i);
			}
			int t = i;
			while (t-- > 0) {
				if ("(".charAt(0) == (controller.charAt(t))) {
					d.add(i);
				}
				if (")".charAt(0) == (controller.charAt(t))) {
					break;
				}
			}
		}
		for (int j = 0; j < b.size(); j++) {
			for (int k = a.size() - 1; k > -1; k--) {
				if (!c.contains(a.get(k)) && b.get(j) > a.get(k)) {
					c.add(a.get(k));
					c.add(b.get(j));
					break;
				}
			}
		}
		for (int l = c.size() - 1; l > 0; l = l - 2) {
			int flag = 0;
			for (int m = 0; m < c.size(); m = m + 2) {
				if ((c.get(m + 1) > c.get(l) && c.get(m) < c.get(l - 1))) {
					flag = 1;
				}
			}
			if (flag == 0 && !d.contains(c.get(l - 1))) {
				String x = controller.substring(0, c.get(l - 1) + 1);
				String y = controller.substring(c.get(l));
				controller = x + y;
			}
		}
		return controller;
	}

	/**
	 * <截取参数string>
	 * 
	 * @param str
	 * @return
	 */
	private static String getParamStr(String str) {
		List<Integer> indexs = new ArrayList<Integer>();
		for (int i = 0; i < str.length(); i++) {
			if ("(".equals(str.charAt(i) + "") || ")".equals(str.charAt(i) + "")) {
				indexs.add(i);
			}
		}
		if (indexs.size() <= 2) {
			return "";
		}
		for (int j = indexs.size() - 2; j > 0; j--) {
			if (")".equals("" + str.charAt(indexs.get(j)))) {
				j--;
			} else {
				return str.substring(indexs.get(j) + 1, indexs.get(indexs.size() - 1));
			}
		}
		return str;
	}

	public static List<String> getRealArr(List<String> tempP, Function<String, Boolean> f1,
			Function<String, Boolean> f2) {
		List<String> arr = new ArrayList<String>();
		for (int t = tempP.size() - 1; t >= 0; t--) {
			if (tempP.get(t) != null && !StringUtils.isEmpty(tempP.get(t)) && f1.apply(tempP.get(t))) {
				int m = t;
				while (m-- > 0) {
					if (tempP.get(m) == null || StringUtils.isEmpty(tempP.get(m))) {
						continue;
					}
					tempP.set(t, tempP.get(m) + "," + tempP.get(t));
					if (f2.apply(tempP.get(t))) {
						t = m;
						tempP.set(m, null);
						break;
					}
					tempP.set(m, null);
				}
			}
		}
		for (int t = tempP.size() - 1; t >= 0; t--) {
			if (!StringUtils.isEmpty(tempP.get(t))) {
				arr.add(tempP.get(t));
			}
		}
		return arr;
	}

	static int countCharAppear(String str, List<Character> chars) {
		int sum = 0;
		for (int i = 0; i < str.length(); i++) {
			if (chars.contains(str.charAt(i))) {
				sum++;
			}
		}
		return sum;
	}

	public static String escapeExprSpecialWord(String keyword) {
		if (!StringUtils.isEmpty(keyword)) {
			String[] fbsArr = { "\\", "$", "(", ")", "*", "+", ".", "[", "]", "?", "^", "{", "}", "|" };
			for (String key : fbsArr) {
				if (keyword.contains(key)) {
					keyword.replaceAll("\\" + key, "\\\\\\\\" + key);
				}
			}
		}
		return keyword;
	}

}
